package com.piece.admin.service;

import com.piece.admin.model.TbSysMenu;
import com.piece.admin.model.TbSysRole;
import com.piece.admin.model.TbSysUser;
import com.piece.admin.repository.TbSysMenuRepository;
import com.piece.admin.support.UserInfoSupport;
import com.piece.core.framework.constant.FrameWorkConstants;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.enums.sql.SqlKey;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.log.client.dto.UserDTO;
import com.piece.core.log.config.UserContextHolder;
import com.piece.core.framework.enums.PermLevelType;
import com.piece.core.jpa.repository.BaseService;
import com.piece.core.framework.support.bootstrap.Order;
import com.piece.core.framework.support.bootstrap.TreeData;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.support.response.BreadCrumb;
import com.piece.core.framework.util.object.ClassUtil;
import com.piece.core.framework.util.string.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.*;

/**
 * 菜单 业务层处理
 */
@Service
@Transactional
public class TbSysMenuService extends BaseService<TbSysMenu, String> {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private TbSysMenuRepository tbSysMenuRepository;

    @PostConstruct
    public void initRepository() {
        setRepository(tbSysMenuRepository);
    }

    /**
     * 查询所有菜单
     */
    @Override
    public List<TbSysMenu> findAll() {
        String hql = "from TbSysMenu order by parentId, orderNum";
        return tbSysMenuRepository.find(hql, null);
    }

    /**
     * 根据用户查询菜单
     */
    public List<TbSysMenu> findByUser(long userId) {
        String sql;
        // 管理员显示所有菜单信息
        if (UserInfoSupport.isAdminRole(userId)) {
            sql = "select distinct m.menu_id menuId, m.parent_id parentId, m.menu_name menuName, m.path path, m.redirect redirect, m.component component, m.target target, m.perms perms, m.menu_type menuType, m.icon icon, m.order_num orderNum, m.create_time createTime " +
                    "from tb_sys_menu m " +
                    "where m.menu_type in ('M', 'C') and m.visible = " + StatusType.VISIBLE.getCode() + " " +
                    "order by m.parent_id asc, m.order_num asc";
        } else {
            sql = "select distinct m.menu_id menuId, m.parent_id parentId, m.menu_name menuName, m.path path, m.redirect redirect, m.component component, m.target target, m.perms perms, m.menu_type menuType, m.icon icon, m.order_num orderNum, m.create_time createTime " +
                    "from tb_sys_menu m " +
                    "left join tb_sys_role_menu rm on m.menu_id = rm.menu_id " +
                    "left join tb_sys_user_role ur on rm.role_id = ur.role_id " +
                    "left join tb_sys_role ro on ur.role_id = ro.role_id " +
                    "where ur.user_id = " + userId + " and m.menu_type in ('M', 'C') and m.visible = " + StatusType.VISIBLE.getCode() + " " +
                    "and ro.status = " + StatusType.NORMAL.getCode() + " " +
                    "order by m.parent_id asc, m.order_num asc";
        }
        return tbSysMenuRepository.findBySql(sql, null);
    }

    /**
     * 根据父节点的ID获取所有子节点
     */
    public List<TbSysMenu> findChildMenus(List<TbSysMenu> list, long parentId) {
        List<TbSysMenu> returnList = new ArrayList<>();
        for (Iterator<TbSysMenu> iterator = list.iterator(); iterator.hasNext(); ) {
            TbSysMenu menu = iterator.next();
            // 根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (menu.getParentId() == parentId) {
                recursionFn(list, menu);
                returnList.add(menu);
            }
        }
        return returnList;
    }

    /**
     * 递归列表
     */
    private void recursionFn(List<TbSysMenu> list, TbSysMenu menu) {
        // 得到子节点列表
        List<TbSysMenu> childList = getChildList(list, menu);
        menu.setChildren(childList);
        for (TbSysMenu tChild : childList) {
            if (hasChild(list, tChild)) {
                // 判断是否有子节点
                Iterator<TbSysMenu> it = childList.iterator();
                while (it.hasNext()) {
                    TbSysMenu n = it.next();
                    recursionFn(list, n);
                }
            }
        }
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<TbSysMenu> list, TbSysMenu menu) {
        return getChildList(list, menu).size() > 0;
    }

    /**
     * 获取子节点列表
     */
    private List<TbSysMenu> getChildList(List<TbSysMenu> list, TbSysMenu menu) {
        List<TbSysMenu> clist = new ArrayList<>();
        Iterator<TbSysMenu> it = list.iterator();
        while (it.hasNext()) {
            TbSysMenu n = it.next();
            if (n.getParentId().longValue() == menu.getMenuId().longValue()) {
                clist.add(n);
            }
        }
        return clist;
    }

    /**
     * 根据用户ID查询权限
     *
     * @param userId 用户ID
     * @return 权限列表
     */
    public Set<String> findPermsByUserId(Long userId) {
        String sql = "select distinct m.perms from tb_sys_menu m " +
                "left join tb_sys_role_menu rm on m.menu_id = rm.menu_id " +
                "left join tb_sys_user_role ur on rm.role_id = ur.role_id " +
                "where m.perms is not null and user_id = " + userId + " ";
        List<String> perms = jdbcTemplate.queryForList(sql, String.class);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            permsSet.addAll(Arrays.asList(perm.trim().split(FrameWorkConstants.SPLIT_PARAM)));
        }
        return permsSet;
    }

    /**
     * 查询菜单树
     */
    public List<TreeData> findTreeData() {
        List<TbSysMenu> menuList = findByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("visible", StatusType.VISIBLE.getCode()).build());
        return findTreeData(menuList, false, null, false);
    }

    /**
     * 对象转菜单树
     *
     * @param menuList     菜单列表
     * @param isCheck      是否需要选中
     * @param roleMenuList 角色已存在菜单列表
     * @param permsFlag    是否需要显示权限标识
     * @return
     */
    public List<TreeData> findTreeData(List<TbSysMenu> menuList, boolean isCheck, List<String> roleMenuList, boolean permsFlag) {
        List<TreeData> trees = new ArrayList<>();
        for (TbSysMenu menu : menuList) {
            TreeData treeData = TreeData.builder()
                    .id(String.valueOf(menu.getMenuId()))
                    .pId(String.valueOf(menu.getParentId()))
                    .title(menu.getMenuName())
                    .name(transMenuName(menu, permsFlag))
                    .build();

            if (isCheck) {
                treeData.setChecked(roleMenuList.contains(menu.getMenuId().toString()));
            } else {
                treeData.setChecked(false);
            }
            trees.add(treeData);
        }
        return trees;
    }

    /**
     * 根据角色ID查询菜单
     *
     * @param role 角色对象
     * @return 菜单列表
     */
    public List<TreeData> findTreeData(TbSysRole role) {
        Long roleId = role.getRoleId();
        List<TreeData> trees;
        List<TbSysMenu> menuList = findByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("visible", StatusType.VISIBLE.getCode()).build());
        if (null != roleId) {
            String sql = "select m.menu_id as perms from tb_sys_menu m " +
                    "left join tb_sys_role_menu rm on m.menu_id = rm.menu_id " +
                    "where m.visible = '0' and rm.role_id =  " + roleId + " " +
                    "order by m.parent_id, m.order_num";
            List<String> roleMenuList = jdbcTemplate.queryForList(sql, String.class);
            trees = findTreeData(menuList, true, roleMenuList, true);
        } else {
            trees = findTreeData(menuList, false, null, true);
        }
        return trees;
    }

    /**
     * 菜单名称添加权限标识
     *
     * @param menu
     * @param permsFlag 是否需要显示权限标识
     * @return name + perm
     */
    public String transMenuName(TbSysMenu menu, boolean permsFlag) {
        StringBuffer sb = new StringBuffer();
        sb.append(menu.getMenuName());
        if (permsFlag) {
            sb.append("<font color=\"#888\">&nbsp;&nbsp;&nbsp;" + Convert.toStr(menu.getPerms(), "") + "</font>");
        }
        return sb.toString();
    }

    /**
     * 查询系统所有权限
     *
     * @return [url, perms]
     */
    public LinkedHashMap<String, String> findPerms() {
        LinkedHashMap<String, String> section = new LinkedHashMap<>();
        List<TbSysMenu> permissions = findAll();
        if (ClassUtil.isNotEmpty(permissions)) {
            for (TbSysMenu menu : permissions) {
                section.put(menu.getPath(), menu.getPerms());
            }
        }
        return section;
    }

    /**
     * 查询子菜单数量
     */
    public int selectCountMenuByParentId(Long parentId) {
        String hql = "from TbSysMenu where parentId = ?0";
        Long count = tbSysMenuRepository.count(hql, new Object[]{parentId}, true);
        return null == count ? 0 : count.intValue();
    }

    /**
     * 查询菜单使用数量
     */
    public int selectCountRoleMenuByMenuId(Long menuId) {
        Integer count = jdbcTemplate.queryForObject("select count(1) from tb_sys_role_menu where menu_id = " + menuId, Integer.class);
        return null == count ? 0 : count;
    }

    /**
     * 校验菜单名称是否唯一
     *
     * @param menuName 菜单名称
     * @return true: 通过 false: 重复
     */
    public boolean checkMenuNameUnique(String menuName) {
        String hql = "from TbSysMenu where menuName = ?0";
        long count = tbSysMenuRepository.count(hql, new Object[]{menuName}, true);
        return count <= 0;
    }

    /**
     * 获取Crumbs路径信息
     *
     * @param path
     * @param user
     * @return
     */
    public List<BreadCrumb> findCrumbsByUrl(String path, TbSysUser user) {
        List<BreadCrumb> result = new ArrayList<>();
        HashMap<String, Object> condition = new HashMap<>();
        condition.put("Q_path_S_EQ", path);
        List<TbSysMenu> menus = tbSysMenuRepository.findByCondition(condition, null);
        if (null == menus || menus.size() < 1) {
            return result;
        }
        TbSysMenu child = menus.get(0);
        menus = findByUser(user.getUserId());
        List<TbSysMenu> returnList = findFatherMenus(menus, child.getMenuId());
        returnList.add(child);
        for (TbSysMenu menu : returnList) {
            BreadCrumb breadCrumb = BreadCrumb.builder()
                    .name(menu.getMenuName())
                    .url(menu.getPath())
                    .icon(menu.getIcon())
                    .build();

            result.add(breadCrumb);
        }
        return result;
    }

    /**
     * 根据父节点的ID获取所有子节点
     */
    public List<TbSysMenu> findFatherMenus(List<TbSysMenu> list, long childId) {
        List<TbSysMenu> returnList = new ArrayList<>();
        for (Iterator<TbSysMenu> iterator = list.iterator(); iterator.hasNext(); ) {
            TbSysMenu menu = iterator.next();
            // 根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (menu.getMenuId().longValue() == childId) {
                reverseRecursionFn(returnList, list, menu);
            }
        }
        return returnList;
    }

    /**
     * 递归列表
     */
    private void reverseRecursionFn(List<TbSysMenu> result, List<TbSysMenu> list, TbSysMenu menu) {
        // 得到父节点
        TbSysMenu father = getFatherMenu(list, menu);
        while (null != father) {
            reverseRecursionFn(result, list, father);
            result.add(father);
            break;
        }
    }

    private TbSysMenu getFatherMenu(List<TbSysMenu> list, TbSysMenu child) {
        Iterator<TbSysMenu> it = list.iterator();
        while (it.hasNext()) {
            TbSysMenu n = it.next();
            if (n.getMenuId().longValue() == child.getParentId().longValue()) {
                return n;
            }
        }
        return null;
    }

    /**
     * 获取访问权限等级
     *
     * @param id
     * @return
     */
    public PermLevelType permLevel(String id) {
        if (StringUtil.isEmpty(id) || "0".equals(id)) {
            return PermLevelType.None;
        }
        TbSysMenu menu = this.findById(Convert.toLong(id));
        if (null == menu) {
            return PermLevelType.None;
        }
        String createBy = menu.getCreateBy();
        UserDTO user = UserContextHolder.get();

        // 管理员拥有权限
        if (null != user && UserInfoSupport.isAdminRole(Convert.toLong(user.getId(), 0L))) {
            return PermLevelType.None;
        }
        // 创建人拥有权限
        if (null != createBy && null != user && createBy.equals(user.getName())) {
            return PermLevelType.None;
        }

        return PermLevelType.Level_ReadOnly;
    }

    /**
     * 获取菜单名称
     *
     * @param ids
     * @return
     */
    public List<String> findMenuNames(Long[] ids) {
        String sql = "select menu_name from tb_sys_menu where menu_id in(";
        for (Long id : ids) {
            sql += "?,";
        }
        sql = sql.substring(0, sql.length() - 1) + ")";
        return jdbcTemplate.queryForList(sql, ids, String.class);
    }

    /**
     * 根据角色获取菜单ID
     *
     * @param roleId
     * @return
     */
    public String findMenuIdByRole(Long roleId) {
        String sql = "select m.menu_id as perms from tb_sys_menu m " +
                "left join tb_sys_role_menu rm on m.menu_id = rm.menu_id " +
                "where rm.role_id =  " + roleId + " " +
                "order by m.parent_id, m.order_num";
        List<String> roleMenuList = jdbcTemplate.queryForList(sql, String.class);
        if (null == roleMenuList || roleMenuList.size() == 0) {
            return null;
        }
        return StringUtil.join(roleMenuList, ",");
    }

    protected List<Order> generateDefaultOrder() {
        List<Order> orders = new ArrayList<>();
        orders.add(new Order("parentId", SqlKey.ASC));
        orders.add(new Order("orderNum", SqlKey.ASC));
        return orders;
    }
}
